package stream.demo02;

import org.junit.Test;

import javax.swing.plaf.PanelUI;
import javax.xml.transform.Source;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

/**
 * @author 真香
 * @create 2021-07-05 0:10
 */
public class demo02 {


    /**
     * filter 过滤
     * 过滤出成绩100分的学生
     */
    @Test
    public void test01(){
        List<Student> students = this.generateSourceData();
        List<Student> list = students.stream().filter(s -> s.getScore() == 100).collect(toList());
        list.forEach(System.out::println);

    }

    /**
     * 查找 findFirst，findAny
     * 一般filter和find搭配使用，即从过滤符合条件的数据中，获得一个数据。
     */
    @Test
    public void test02(){
        List<Student> students = this.generateSourceData();
        Optional<Student> studentOptional = students.stream().filter(s -> s.getScore() >= 20).findFirst();
        if(studentOptional.isPresent()) {
            Student student = studentOptional.get();
            System.out.println(student);
        }
    }

    /**
     * 匹配 match
     *
     */
    @Test
    public void test03(){
        // 是否存在100分的学生
        List<Student> students = this.generateSourceData();
        boolean anyMatch = students.stream().anyMatch(student -> student.getScore() == 100);
        // 是否全部学生都100分
        boolean allMatch = students.stream().allMatch(student -> student.getScore() == 100);
        // 是否全部学生都没有100分
        boolean noneMatch = students.stream().noneMatch(student -> student.getScore() == 100);
        System.out.println(anyMatch);
        System.out.println(allMatch);
        System.out.println(noneMatch);
    }


    /**
     *  映射 map
     *  映射，顾名思义，将一个对象映射成另外一个对象。即一个Stream流中的所有元素按照
     *      一定的映射规则，映射到另一个流中。映射有map和flatMap两种类型：
     *  map：接收一个函数作为参数，此函数作用到Stream中每一个元素，形成一个新的元素，所有新的元素组成一个新的流。
     *  flatMap：接收一个函数作为参数，它将流中的每个元素都转换成另一个流，然后把所有流再连接形成一个最终流。
     */
    @Test
    public void test04(){
        List<Student> students = this.generateSourceData();
        // 获取每个学生的姓名
        List<String> nameList = students.stream().map(Student::getName).collect(toList());
        //System.out.println(nameList);
        // 每个学生的成绩加10分
        List<Double> scoreList = students.stream().map(student -> student.getScore() + 10).collect(toList());
        //System.out.println(scoreList);

        List<String> list = Arrays.asList("a-b-c-d", "g-h-i");
        List<String> list1 = list.stream().flatMap(s -> Arrays.stream(s.split("-"))).collect(Collectors.toList());
        System.out.println(list1);
    }


    /**
     *截取流 limit
     * limit方法用于获取指定数量的流
     *
     */
    @Test
    public void test05(){
        List<Student> students = this.generateSourceData();
        // 取出学习成绩大于70的5个人
        //List<Student> list = students.stream().filter(student -> student.getScore() > 70).limit(5).collect(toList());
        //list.forEach(System.out::println);

        students.stream().filter(student -> student.getScore() > 70).limit(5).forEach(System.out::println);

        // 跳过2个再取一个
        List<Student> list1 = students.stream().skip(2).limit(1).collect(toList());

        // 获取5个int随机数 从小排序到大
        Random random = new Random();
        //random.ints().limit(5).sorted().forEach(System.out::println);
    }


    /**
     * 排序 sorted
     * sorted 方法用于对流中的元素进行排序，有两种排序：
     * sorted()：自然排序，流中元素需要实现Comparable接口。
     * sorted(Comparator<? super T> comparator)：需要自定义排序器。
     *
     */
    @Test
    public void test06(){
        List<Student> students = this.generateSourceData();
        // 按成绩升序
        List<Student> list = students.stream().sorted(Comparator.comparing(Student::getScore)).collect(toList());
        System.out.println("按成绩升序");
        list.forEach(System.out::println);

        // 按成绩降序
        List<Student> list2 = students.stream().sorted(Comparator.comparing(Student::getScore).reversed()).collect(toList());
        System.out.println("按成绩降序");
        list2.forEach(System.out::println);

        // 按成绩升序，再按年龄升序
        List<Student> list3 = students.stream().sorted(Comparator.comparing(Student::getScore).thenComparing(Student::getAge)).collect(toList());
        System.out.println("按成绩升序，再按年龄升序");
        list3.forEach(System.out::println);

        // 按成绩升序，再按年龄降序
        List<Student> list4 = students.stream().sorted((s1, s2) -> {
            if (s1.getScore() != s2.getScore()) {
                return (int) (s1.getScore() - s2.getScore());
            } else {
                return (s2.getAge() - s1.getAge());
            }
        }).collect(toList());
        System.out.println("按成绩升序，再按年龄降序");
        list4.forEach(System.out::println);

    }


    /**
     * 去重 distinct
     *
     */
    @Test
    public void test07(){
        List<String> list2 = Arrays.asList("a", "b", "a", "c", "f", "e", "f");
        List<String> list = list2.stream().distinct().collect(toList());
        list.forEach(System.out::print);
        System.out.println();
        // set集合特性不能存在重复元素
        Set<String> sets = list.stream().collect(toSet());
        sets.forEach(System.out::print);
    }


    /**
     * 统计 summaryStatistics
     */
    @Test
    public void test08(){
        List<Student> students = this.generateSourceData();
        DoubleSummaryStatistics summaryStatistics = students.stream().mapToDouble(Student::getScore).summaryStatistics();
        System.out.println("平均值: "+summaryStatistics.getAverage());
        System.out.println("总个数："+summaryStatistics.getCount());
        System.out.println("最大值："+summaryStatistics.getMax());
        System.out.println("最小值："+summaryStatistics.getMin());
        System.out.println("总和值："+summaryStatistics.getSum());

        // 统计个数
        long count = students.stream().count();
        // 平均值
        Double averageScore =
                students.stream().collect(Collectors.averagingDouble(Student::getScore));

        // 最大值和最小值
        Optional<Double> maxScore = students.stream().map(Student::getScore).max(Double::compare);
        Optional<Double> minScore = students.stream().map(Student::getScore).min(Double::compare);

        // 求和
        double sumScore = students.stream().mapToDouble(Student::getScore).sum();

        // 一次性统计所有
        DoubleSummaryStatistics doubleSummaryStatistics1 =
                students.stream().collect(Collectors.summarizingDouble(Student::getScore));
        System.out.println("单个维度计算：");
        System.out.println("统计个数：" + count);
        System.out.println("平均值：" + averageScore);
        maxScore.ifPresent(aDouble -> System.out.println("最大值：" + aDouble));
        minScore.ifPresent(aDouble -> System.out.println("最小值：" + aDouble));
        System.out.println("求和：" + sumScore);
        System.out.println("一次性统计所有：" + doubleSummaryStatistics1);

    }


    /**
     * 归约 reduce
     * 归约，把一个流归约(缩减)成一个值，能实现对集合求和、求乘积和求最值等操作。
     *
     */
    @Test
    public void test09(){

        List<Integer> integerList = Arrays.asList(6, 7, 1, 10, 11, 7, 13, 20);
        // 求和
        Optional<Integer> sum1 = integerList.stream().reduce(Integer::sum);
        // 求和,基于10的基础上
        Integer sum2 = integerList.stream().reduce(10, Integer::sum);
        // 求最大值
        Optional<Integer> max1 = integerList.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值,基于与50比较的基础上
        Integer max2 = integerList.stream().reduce(50, Integer::max);
        Optional<Integer> min = integerList.stream().reduce(Integer::min);
        // 求乘积
        Optional<Integer> product = integerList.stream().reduce((x, y) -> x * y);
        System.out.println("原始集合：" + integerList);
        System.out.println("集合求和：" + sum1.get() + "," + sum2);
        System.out.println("集合求最大值：" + max1.get() + "," + max2);
        System.out.println("集合求最小值：" + min.get());
        System.out.println("集合求积：" + product.get());

    }


    @Test
    public void test10(){

        List<Student> students = this.generateSourceData();
        // 按条件学生成绩是否大于等于60，划分为2个组
        Map<Boolean, List<Student>> studentScorePart = students.stream()
                .collect(Collectors.partitioningBy(student -> student.getScore() >= 60));
// 按性别分组
        Map<String, List<Student>> studentSexMap =
                students.stream().collect(Collectors.groupingBy(Student::getSex));
// 按年龄分组
        Map<Integer, List<Student>> studentAgeMap =
                students.stream().collect(Collectors.groupingBy(Student::getAge));
// 先按性别分组，再按年龄分组
        Map<String, Map<Integer, List<Student>>> collect = students.stream().collect(
                Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAge)));
        System.out.println("按条件学生成绩是否大于等于60，划分为2个组:");
        studentScorePart.forEach((aBoolean, students7) -> {
            System.out.println("成绩大于等于60?:" + aBoolean);
            students7.forEach(System.out::println);
        });
        System.out.println("按性别分组:");
        studentSexMap.forEach((sex, students7) -> {
            System.out.println("性别?:" + sex);
            students7.forEach(System.out::println);
        });
        System.out.println("按年龄分组:");
        studentAgeMap.forEach((age, students7) -> {
            System.out.println("年龄:" + age);
            students7.forEach(System.out::println);
        });
        System.out.println("先按性别分组，再按年龄分组:");
        collect.forEach((sex, integerListMap) -> {
            System.out.println("性别：" + sex);
            integerListMap.forEach((age, students7) -> {
                System.out.println("年龄:" + age);
                students7.forEach(System.out::println);
            });
        });
    }


    @Test
    public void test11(){
        List<Student> students = this.generateSourceData();
        String joinName = students.stream().map(Student::getName).collect(Collectors.joining(", "));
        System.out.println(joinName);


    }

    // 生产数据源
    public List<Student> generateSourceData(){

        List<Student> students = new ArrayList<>(16);
        students.add(new Student("1", "张三", 18, "male", 88));
        students.add(new Student("2", "李四", 17, "male", 60));
        students.add(new Student("3", "王五", 18, "male", 100));
        students.add(new Student("4", "赵六", 20, "male", 10));
        students.add(new Student("5", "董七", 14, "female", 95));
        students.add(new Student("6", "幺八", 21, "male", 55));
        students.add(new Student("7", "老九", 20, "female", 66));
        students.add(new Student("8", "小明", 18, "male", 100));
        students.add(new Student("9", "小红", 22, "female", 95));
        students.add(new Student("10", "小张", 25, "male", 90));
        return students;
    }




}
